<template>
  <div class="uploader-file" :status="status">
    <slot
      :file="file"
      :list="list"
      :status="status"
      :paused="paused"
      :error="error"
      :response="response"
      :average-speed="averageSpeed"
      :formated-average-speed="formatedAverageSpeed"
      :current-speed="currentSpeed"
      :is-complete="isComplete"
      :is-uploading="isUploading"
      :size="size"
      :formated-size="formatedSize"
      :uploaded-size="uploadedSize"
      :progress="progress"
      :progress-style="progressStyle"
      :progressing-class="progressingClass"
      :time-remaining="timeRemaining"
      :formated-time-remaining="formatedTimeRemaining"
      :type="type"
      :extension="extension"
      :file-category="fileCategory"
      :merger-progress="mergerProgress"
      >
      <div class="uploader-file-progress" :class="progressingClass" :style="progressStyle"></div>
      <div class="uploader-file-info">
        <div class="uploader-file-name"><i class="uploader-file-icon" :icon="fileCategory"></i>{{file.name}}</div>
        <div class="uploader-file-size">{{formatedSize}}</div>
        <div class="uploader-file-meta"></div>
        <div class="uploader-file-status">
          <span v-show="status !== 'uploading'">{{statusText}}<span class="dotting"></span></span>
          
          <span v-show="status === 'uploading'">
            <span>{{progressStyle.progress}}</span>
            <em>{{formatedAverageSpeed}}</em>
            <i>{{formatedTimeRemaining}}</i>
          </span>
        </div>
       
        <div class="uploader-file-actions">
          <span class="uploader-file-pause" @click="pause"></span>
          <span class="uploader-file-resume" @click="resume"></span>
          <span class="uploader-file-retry" @click="retry"></span>
          <span class="uploader-file-errorMerge" @click="handleretryMerge"></span>
          <span class="uploader-file-remove" @click="remove"></span>
        </div>
      </div>
    </slot>
  </div>
</template>

<script>
  import Uploader from 'simple-uploader.js'
  import events from '../common/file-events'
  import { secondsToStr,defautlCategoryMap } from '../common/utils'
  import { uploaderMixin } from '../common/mixins'
  import SparkMD5 from 'spark-md5'
  const COMPONENT_NAME = 'uploader-file'

  import { Modal,message } from 'ant-design-vue';
  import {mergerFile, downloadAttach, bindBusKey, deleteAttach, getAttaches,deleteChunks } from '../api/upload'
  export default {
    name: COMPONENT_NAME,
    mixins: [uploaderMixin],
    props: {
      file: {
        type: Object,
        default () {
          return {}
        }
      },
      list: {
        type: Boolean,
        default: false
      }
    },
    data () {
      return {
        response: null,
        paused: false,
        error: false,
        averageSpeed: 0,
        currentSpeed: 0,
        isComplete: false,
        isUploading: false,
        size: 0,
        formatedSize: '',
        uploadedSize: 0,
        progress: 0,
        timeRemaining: 0,
        type: '',
        extension: '',
        progressingClass: '',
        mergerProgress:0,
        errorMerge:false,
        
      }
    },
    computed: {
      fileCategory () {
        const extension = this.extension
        const isFolder = this.file.isFolder
        let type = isFolder ? 'folder' : 'unknown'
        const categoryMap = this.file.uploader.opts.categoryMap
        const typeMap = categoryMap ||defautlCategoryMap
        Object.keys(typeMap).forEach((_type) => {
          const extensions = typeMap[_type]
          if (extensions.indexOf(extension) > -1) {
            type = _type
          }
        })
        return type
      },
      progressStyle () {
        const progress = Math.floor(this.progress * 100)
        const style = `translateX(${Math.floor(progress - 100)}%)`
        return {
          progress: `${progress}%`,
          webkitTransform: style,
          mozTransform: style,
          msTransform: style,
          transform: style
        }
      },
      formatedAverageSpeed () {
        return `${Uploader.utils.formatSize(this.averageSpeed)} / 秒`
      },
      formatedAverageSpeed () {
        return `${Uploader.utils.formatSize(this.averageSpeed)} / 秒`
      },
      
      status () {
        const isComputeMd5 = this.isComputeMd5
        const isUploading = this.isUploading
        const isComplete = this.isComplete
        const isError = this.error
        const paused = this.paused
        const errorMerge = this.errorMerge
        if(isComputeMd5){
          return 'computeMd5'
        }
        else if  (isComplete) {
          return 'success'
        } else if (isError) {
          return 'error'
        } else if (isUploading) {
          return 'uploading'
        } else if (paused) {
          return 'paused'
        } else if (errorMerge){
            return 'errorMerge'
        }else{
          return 'waiting'
        }
      },
      statusText () {
        const status = this.status
        const fileStatusText = this.fileStatusText || this.file.uploader
        let txt = status
        if (typeof fileStatusText === 'function') {
          txt = fileStatusText(status, this.response)
        }else {
          txt = fileStatusText[status]
        }
        return txt || status
      },
      formatedTimeRemaining () {
        const timeRemaining = this.timeRemaining
        const file = this.file
        if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
          return ''
        }
        let parsedTimeRemaining = secondsToStr(timeRemaining)
        const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
        if (parseTimeRemaining) {
          parsedTimeRemaining = parseTimeRemaining(timeRemaining, parsedTimeRemaining)
        }
        return '剩余时间'+parsedTimeRemaining
      }
    },
    watch: {
      status (newStatus, oldStatus) {
        if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
          this.tid = setTimeout(() => {
            this.progressingClass = 'uploader-file-progressing'
          }, 200)
        } else {
          clearTimeout(this.tid)
          this.progressingClass = ''
        }
      }
    },
    methods: {
      _actionCheck () {
        this.paused = this.file.paused
        this.error = this.file.error
        
        this.isUploading = this.file.isUploading()
        this.isComputeMd5 = this.file.isComputeMd5
      },
      pause () {
        this.file.pause()
        this._actionCheck()
        this._fileProgress()
      },
      resume () {
        this.file.resume()
        this._actionCheck()
      },
      remove () {
        let params = this.uploader.processParams(this.file)  

        if(this.computeMD5){
          this.file.cancel()
          return;
        }
        this.file.cancel()
        params.deleteChunks=[params]
        deleteChunks(params).then(res=>{
          this.file.cancel()
        }).catch(res=>{ 
            // this._fileError(null,null,JSON.stringify(res))
        })
        
      },
      retry () {

        this.file.retry()
        this._actionCheck()
      },
      processResponse (message) {
        let res = message
        try {
          res = JSON.parse(message)
        } catch (e) {}
        this.response = res
      },
      fileEventsHandler (event, args) {
        const rootFile = args[0]
        const file = args[1]
        const target = this.list ? rootFile : file
        if (this.file === target) {
          if (this.list && event === 'fileSuccess') {
            this.processResponse(args[2])
            return
          }
          this[`_${event}`].apply(this, args)
        }
      },
      _fileProgress () {
        this.progress = this.file.progress()
        this.averageSpeed = this.file.averageSpeed
        this.currentSpeed = this.file.currentSpeed
        this.timeRemaining = this.file.timeRemaining()
        this.uploadedSize = this.file.sizeUploaded()
        this.uploader.onProgress(this.file,this.progress,this.averageSpeed,this.timeRemaining,this.uploadedSize)
        this._actionCheck()
      },
      _fileSuccess (rootFile, file, response,chunk) {
        //文件块上传完成事件
        if (rootFile) {
          this.processResponse(response)
        }
        this._fileProgress()
        this.error = false
        this.isComplete = true
        this.isUploading = false
        this.computeMD5 = false
        this.errorMerge = false
        let params = this.uploader.processParams(file)
      
        mergerFile(params).then(res=>{
          if(res.success){
            let data =res.result
             message.success(`${file.name}文件上传成功 `)
            
           this.file.cancel();
           this.uploader.fileListDefault.unshift(data);
          
            this.uploader.onSuccess(res,data,this.uploader.fileListDefault)
            
            this.uploader.onChange(data, this.uploader.fileListDefault);
          }else{
            this._fileErrorMerger()
          }
           
        }).catch(res=>{
          //  this._fileError(null,null,JSON.stringify(res||{}))
           this._fileErrorMerger()
          // message.error(`合并${file.name}文件失败`)
        })
      },
      handleretryMerge(){
        this._fileSuccess(null,this.file,null,null)
      },
     _fileErrorMerger(){
        this.error = false
        this.isComplete = false
        this.isUploading = false
        this.computeMD5 = false
        this.errorMerge = true
     },
      _fileComplete (rootFile) {
        
        //文件夹上传直接执行_fileSuccess
        //文件根、文件夹完成事件
        //不需要执行
        // this._fileSuccess()
        
      },
      
      _fileError (rootFile, file, message) {
        //文件错误
        // this.file.uploader.off('fileSuccess', this._handlers['fileSuccess'])
       
        if(!!message){
        this.processResponse(message)

        }
        this.error = true
        this.isComplete = false
        this.isUploading = false
        this.computeMD5 = false
         this.errorMerge = false
        this.uploader.onError(this.response,file,this.uploader.fileListDefault)
        this.uploader.onChange(file, this.uploader.fileListDefault);
      },
       computeMD5(file) {
         let _this = this
         //计算文件md5
                let fileReader = new FileReader();
                let time = new Date().getTime();
                let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
                let currentChunk = 0;
                const chunkSize = file.uploader.opts.chunkSize;
                let chunks = Math.ceil(file.size / chunkSize);
                let spark = new SparkMD5.ArrayBuffer();

                

                loadNext();

                fileReader.onload = (e => {
                    spark.append(e.target.result);

                    if (currentChunk < chunks) {
                        currentChunk++;
                        loadNext();
                        this.isComputeMd5=true
                        // 实时展示MD5的计算进度
                        this.$nextTick(() => {
                        
                        this.file.isComputeMd5 = true
                        
                        this.fileStatusText.computeMd5 ='文件校验中'   + ((currentChunk/chunks)*100).toFixed(0)+'%';
                        this.pause()
                        })
                        
                    } else {
                      
                        let md5 = spark.end();
                        this.isComputeMd5=false
                        this.file.isComputeMd5 = false
                        //是否允许重复上传默认不允许
                        if(!this.uploader.uploader.opts.allowDuplicateUploads){
                         let fileDefault = this.uploader.fileListDefault.find(item=>{
                             return  item.identifier == md5    
                          })
                          if(!!fileDefault){
                            setTimeout(function(){
                              _this.remove()
                            },800)
                            
                            message.error(`上传${fileDefault.fileName}文件重复`)
                            return
                          }
                          let fileUploading = this.uploader.fileList.find(item=>{
                             return  item.fileMd5 == md5    
                          })
                          if(!!fileUploading){
                             setTimeout(function(){
                              _this.remove()
                            },800)
                            message.error(`上传${fileUploading.name}文件重复,已在上传列表中`)
                            return
                          }
                        } 
                        this.file.fileMd5 = md5
                        //开始下载
                        this.resume();
                    }
                    
                });

                fileReader.onerror = function () {
                    //this.error(`校验文件${file.name}读取出错，请检查该文件`)
                    file.cancel();
                };

                function loadNext() {
                    let start = currentChunk * chunkSize;
                    let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

                    fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
                }
            },
    },
    mounted () {
      const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed','isComputeMd5']
      const staticUploaderProps = ['fileStatusText']
      const fnProps = [
        
        'isComplete',
        'isUploading',
        {
          key: 'size',
          fn: 'getSize'
        },
        {
          key: 'formatedSize',
          fn: 'getFormatSize'
        },
        {
          key: 'uploadedSize',
          fn: 'sizeUploaded'
        },
        'progress',
        'timeRemaining',
        {
          key: 'type',
          fn: 'getType'
        },
        {
          key: 'extension',
          fn: 'getExtension'
        }
      ]
      staticProps.forEach(prop => {
        this[prop] = this.file[prop]
      })
       staticUploaderProps.forEach(prop => {
        this[prop] = this.file.uploader[prop]
        console.log('_'+prop)
      })
      fnProps.forEach((fnProp) => {
        if (typeof fnProp === 'string') {
          this[fnProp] = this.file[fnProp]()
        } else {
          this[fnProp.key] = this.file[fnProp.fn]()
        }
      })

      const handlers = this._handlers = {}
      const eventHandler = (event) => {
        handlers[event] = (...args) => {
          this.fileEventsHandler(event, args)
        }
        return handlers[event]
      }
      events.forEach((event) => {
        this.file.uploader.on(event, eventHandler(event))
      })
      this.computeMD5(this.file)
    },
    destroyed () {
      events.forEach((event) => {
        this.file.uploader.off(event, this._handlers[event])
      })
      this._handlers = null
    }
  }
</script>

<style>
  .uploader-file {
    position: relative;
    height: 49px;
    line-height: 49px;
    overflow: hidden;
    border-bottom: 1px solid #e8e8e8;
  }
  .uploader-file[status="waiting"] .uploader-file-pause,
  .uploader-file[status="uploading"] .uploader-file-pause {
    display: block;
  }
  .uploader-file[status="paused"] .uploader-file-resume {
    display: block;
  }
  .uploader-file[status="error"] .uploader-file-retry {
    display: block;
  }
  .uploader-file[status="errorMerge"] .uploader-file-errorMerge {
    display: block;
  }
  .uploader-file[status="success"] .uploader-file-remove {
    display: none;
  }
  .uploader-file[status="error"] .uploader-file-progress , .uploader-file[status="errorMerge"] .uploader-file-progress {
    background: #ffe0e0;
  }
  .uploader-file-progress {
    position: absolute;
    width: 100%;
    height: 100%;
    background: #e2eeff;
    transform: translateX(-100%);
  }
  .uploader-file-progressing {
    transition: all .4s linear;
  }
  .uploader-file-info {
    position: relative;
    z-index: 1;
    height: 100%;
    overflow: hidden;
  }
  .uploader-file-info:hover {
    background-color: rgba(240, 240, 240, 0.2);
  }
  .uploader-file-info i,
  .uploader-file-info em {
    font-style: normal;
  }
  .uploader-file-name,
  .uploader-file-size,
  .uploader-file-meta,
  .uploader-file-status,
  .uploader-file-actions {
    float: left;
    position: relative;
    height: 100%;
  }
  .uploader-file-name {
    width: 45%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-indent: 14px;
  }
  .uploader-file-icon {
    width: 24px;
    height: 24px;
    display: inline-block;
    vertical-align: top;
    margin-top: 13px;
    margin-right: 8px;
  }
  .uploader-file-icon::before {
    content: "📃";
    display: block;
    height: 100%;
    font-size: 24px;
    line-height: 1;
    text-indent: 0;
  }
  .uploader-file-icon[icon="folder"]::before {
    content: "📂";
  }
  .uploader-file-icon[icon="image"]::before {
    content: "📊";
  }
  .uploader-file-icon[icon="video"]::before {
    content: "📹";
  }
  .uploader-file-icon[icon="audio"]::before {
    content: "🎵";
  }
  .uploader-file-icon[icon="document"]::before {
    content: "📋";
  }
  .uploader-file-size {
    width: 13%;
    text-indent: 10px;
  }
  .uploader-file-meta {
    width: 8%;
  }
  .uploader-file-status {
    width: 24%;
    text-indent: 20px;
  }
  .uploader-file-actions {
    width: 10%;
  }
  .uploader-file-actions > span {
    display: none;
    float: left;
    width: 16px;
    height: 16px;
    margin-top: 16px;
    margin-right: 10px;
    cursor: pointer;
    background: url("") no-repeat 0 0;
  }
  .uploader-file-actions > span:hover {
    background-position-x: -21px;
  }
  .uploader-file-actions .uploader-file-pause {
    background-position-y: 0;
  }
  .uploader-file-actions .uploader-file-resume {
    background-position-y: -17px;
  }
  .uploader-file-actions .uploader-file-retry,.uploader-file-actions .uploader-file-errorMerge {
    background-position-y: -53px;
  }

  
  .uploader-file-actions .uploader-file-remove {
    display: block;
    background-position-y: -34px;
  }

 .uploader-file[status="success"] .dotting{
   display: inline-block;
 }
.dotting {

display: none; width: 10px; min-height: 2px;
padding-right: 2px;
border-left: 2px solid currentColor; border-right: 2px solid currentColor;
background-color: currentColor; background-clip: content-box;
box-sizing: border-box;
-webkit-animation: dot 4s infinite step-start both;
animation: dot 4s infinite step-start both;

}
.dotting:before { content: '...'; } /* IE8 */
.dotting::before { content: ''; }
:root .dotting { margin-left: 2px; padding-left: 2px; } /* IE9+ */

@-webkit-keyframes dot {
25% { border-color: transparent; background-color: transparent; }
50% { border-right-color: transparent; background-color: transparent; }
75% { border-right-color: transparent; }
}
@keyframes dot {
25% { border-color: transparent; background-color: transparent; }
50% { border-right-color: transparent; background-color: transparent; }
75% { border-right-color: transparent; }
}
</style>
